package main

import (
	"flag"
	"os"
	"os/signal"
	"path/filepath"
	"sync/atomic"
	"syscall"
	"time"

	"jgt-sdk/asapi"
	"jgt-wallet/src/logger"
	"jgt-wallet/src/model/mgodb"
	"jgt-wallet/src/model/mydb"
	"jgt-wallet/src/router"
	"jgt-wallet/src/service/db"
	"jgt-wallet/src/service/mongo"
	"jgt-wallet/src/util"

	mongohook "github.com/LyricTian/logrus-mongo-hook"
	"github.com/facebookgo/inject"
	"github.com/spf13/viper"
	"github.com/teambition/gear"
)

const (
	// Version 版本号
	Version = "v1.0.0"
)

var (
	config  = flag.String("config", "", "配置文件(.json,.yaml,.toml)")
	traceID string
)

func main() {
	flag.Parse()

	if v := *config; v == "" {
		//		panic("请使用-config指定配置文件")
		*config = "../../config/config.toml"
	}

	// 初始化配置文件
	viper.SetConfigFile(*config)
	if err := viper.ReadInConfig(); err != nil {
		panic("读取配置文件错误：" + err.Error())
	}

	traceID = util.UUID()

	// 初始化授权API
	initAuthAPI()

	// 初始化日志
	loggerFlush := initLogger()

	// 初始化支付客户端
	initClient()

	// 初始化依赖注入
	g := new(inject.Graph)

	var (
		db dber
	)

	switch viper.GetString("store_type") {
	case "mysql":
		mysqlDB := initMySQLDB()
		mydb.RegisterMySQL(g, mysqlDB)
		db = mysqlDB
	case "mongo":
		mgoDB := initMongoDB()
		mgodb.RegisterMongo(g, mgoDB)
		db = mgoDB
	}

	if v := viper.GetString("run_mode"); v == util.DevMode {
		logger.System(traceID).Warnf("服务运行在开发模式，请知晓！！！")
	}

	logger.System(traceID).Infof("开始运行服务，当前版本:%s，进程号：%d", Version, os.Getpid())

	var state int32 = 1
	ac := make(chan error)
	sc := make(chan os.Signal, 1)

	signal.Notify(sc,
		syscall.SIGTERM,
		syscall.SIGQUIT)

	appServer := initAppServer(g)
	go func() {
		addr := viper.GetString("http_addr")
		logger.System(traceID).Infof("HTTP服务启动成功，端口监听在[%s]", addr)
		ac <- appServer.Listen(addr)
	}()

	if err := g.Populate(); err != nil {
		logger.System(traceID).WithError(err).Panic("注入模块发生错误")
	}

	select {
	case err := <-ac:
		if err != nil && atomic.LoadInt32(&state) == 1 {
			logger.System(traceID).Errorf("监听HTTP服务发生错误:%s", err.Error())
		}
	case sig := <-sc:
		atomic.StoreInt32(&state, 0)
		logger.System(traceID).Infof("获取到退出信号[%s]", sig.String())
	}

	// 关闭HTTP服务
	if err := appServer.Close(); err != nil {
		logger.System(traceID).Errorf("关闭HTTP服务发生错误:%s", err.Error())
	}

	// 关闭mysql连接
	if err := db.Close(); err != nil {
		logger.System(traceID).Errorf("关闭mysql连接发生错误:%s", err.Error())
	}

	if loggerFlush != nil {
		loggerFlush.Flush()
	}

	os.Exit(int(atomic.LoadInt32(&state)))
}

// 初始化授权API
func initAuthAPI() {
	auth := viper.GetStringMap("auth")
	asapi.InitAPI(&asapi.Config{
		ASURL:           util.TT(auth["addr"]).String(),
		ClientID:        util.TT(auth["client_id"]).String(),
		ClientSecret:    util.TT(auth["client_secret"]).String(),
		ServiceIdentify: util.TT(auth["identify"]).String(),
	})
}

// 初始化app服务
func initAppServer(g *inject.Graph) *gear.App {
	// 初始化HTTP服务
	app := gear.New()

	// 设定错误处理
	app.Set(gear.SetOnError, func(ctx *gear.Context, err gear.HTTPError) {
		ctx.JSON(err.Status(), map[string]interface{}{
			"code":    0,
			"message": err.Error(),
		})
	})

	apiPrefixes := []string{
		"/wallet/web/v1/",
		"/wallet/svc/v1/",
		"/srv/v1/",
		"/api/v1/",
	}

	// 跟踪ID
	app.Use(router.TraceIDMiddleware(apiPrefixes...))

	// APP参数
	app.Use(router.APPParameterMiddleware())

	// 注册日志中间件
	app.Use(logger.Middleware(apiPrefixes...))
	// 注册Web路由中间件
	app.UseHandler(router.WebRouterV1Handler(g))
	// 注册Svc路由中间件
	app.UseHandler(router.SvcRouterV1Handler(g))
	// 注册Srv路由中间件
	app.UseHandler(router.SrvRouterV1Handler(g))
	// 注册API路由中间件
	app.UseHandler(router.APIRouterV1Handler(g))

	// 注册Dev路由中间件
	app.UseHandler(router.DevRouterV1Handler(g))

	// 注册静态站点中间件
	if v := viper.GetString("web_path"); v != "" {
		vv, err := filepath.Abs(v)
		if err != nil {
			logger.System(traceID).WithError(err).Panic("获取静态站点目录发生错误")
		}
		app.Use(router.WwwHandler(vv, "/wallet/web/v1", "/wallet/svc/v1", "/srv/v1/", "/api/v1/"))
		logger.System(traceID).Infof("开启app的静态站点服务：%s", v)
	}

	return app
}

// 初始化日志
func initLogger() logger.HookFlusher {
	log := logger.New(
		logger.SetFormat(viper.GetString("log_format")),
		logger.SetLevel(viper.GetInt("log_level")),
	)

	// 启用日志钩子写入到数据库
	if viper.GetBool("log_db_enable") {
		logCfg := viper.GetStringMap("log")
		hook := mongohook.DefaultWithURL(
			util.TT(logCfg["url"]).String(),
			util.TT(logCfg["db"]).String(),
			util.TT(logCfg["c"]).String(),
			mongohook.SetMaxQueues(util.TT(logCfg["max_buffer"]).Int()),
			mongohook.SetMaxWorkers(util.TT(logCfg["max_thread"]).Int()),
			mongohook.SetExtra(map[string]interface{}{
				"system": util.TT(logCfg["system"]).String(),
			}),
		)
		log.AddHook(hook)
		return hook
	}

	return nil
}

// 初始化数据库
func initMySQLDB() *db.DB {
	mmysql := viper.GetStringMap("mysql")
	trace := util.TT(mmysql["trace"]).Bool()

	db, err := db.NewMySQL(
		db.SetTrace(trace),
		db.SetDSN(util.TT(mmysql["dsn"]).String()),
		db.SetMaxLifetime(time.Duration(util.TT(mmysql["max_lifetime"]).Int64())),
		db.SetMaxIdleConns(util.TT(mmysql["max_idle_conns"]).Int()),
		db.SetMaxOpenConns(util.TT(mmysql["max_open_conns"]).Int()),
	)

	if err != nil {
		logger.System(traceID).Panic("初始化数据库发生错误:" + err.Error())
	}
	return db
}

// 初始化mongo数据库
func initMongoDB() *mongo.DB {
	mconfig := viper.GetStringMap("mongo")

	db, err := mongo.NewDB(
		util.TT(mconfig["url"]).String(),
		util.TT(mconfig["db"]).String(),
		0)

	if err != nil {
		logger.System(traceID).Panic("初始化Mongo数据库发生错误:" + err.Error())
	}

	return db
}

type dber interface {
	Close() error
}

type hooker interface {
	Flush()
}
